package middleware

import (
	"fmt"
	"hello-gin/config"
	"io"
	"os"
	"path"
	"time"

	"github.com/gin-gonic/gin"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	lfshook "github.com/rifflock/lfshook"
	logrus "github.com/sirupsen/logrus"
)

var (
	// 初始化日志对象
	Logger   = logrus.New()
	LogEntry *logrus.Entry
)

// 日志记录到文件
func LoggerToFile() gin.HandlerFunc {

	// 获取配置文件中日志路径与日志文件名称
	logFilePath := config.Log_FILE_PATH
	logFileName := config.LOG_FILE_NAME

	// 日志文件路径
	infologFile := path.Join(logFilePath, logFileName+".log")
	errorlogFile := path.Join(logFilePath, logFileName+"-error.log")
	debuglogFile := path.Join(logFilePath, logFileName+"-debug.log")

	// 写入日志到Info/Debug/Error日志等级对应文件
	f, err := os.OpenFile(infologFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModeAppend)
	if err != nil {
		fmt.Println("Failed to create logfile" + infologFile)
		panic(err)
	}
	// defer f.Close()

	fdebug, err := os.OpenFile(debuglogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModeAppend)
	if err != nil {
		fmt.Println("Failed to create logfile" + debuglogFile)
		panic(err)
	}
	// defer fdebug.Close()

	ferr, err := os.OpenFile(errorlogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModeAppend)
	if err != nil {
		fmt.Println("Failed to create logfile" + errorlogFile)
		panic(err)
	}
	// defer ferr.Close()

	// 同时将日志分别输出到文件和屏幕中
	writers := []io.Writer{
		f, fdebug, ferr,
		os.Stdout}
	log := io.MultiWriter(writers...)
	Logger.SetOutput(log)

	// 设置输出到屏幕终端的日志格式
	Logger.SetFormatter(&logrus.TextFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
		// ForceColors:     true,
		// DisableSorting:   true,
		// ForceQuote:       true,
		QuoteEmptyFields: true,
	})

	// 设置logrus默认的日志级别,日志级别由 Panic 级别最高， Trace 级别最低
	// Panic：恐慌级别，也是最高级别的日志，会打印出错误堆栈。
	// Fatal：致命错误，输出日志后，执行 exit(1) 退出
	// Error：错误日志，必须记录与跟踪的日志，一般是系统报错需要开发接入。
	// Warn：警告日志，主要记录需要提醒开发者的日志，一般提示开发者注意一哈
	// Info：主要是提供一些必要的日志信息，在业务出现问题时，可以结合error日志快速定位问题，一般会默认使用该级别的日志。
	// Debug：调试信息，方便开发测试阶段的问题定位, 一般是中间调试使用。
	// Trace：比 debug 级别还低，一般很少用。
	Logger.SetLevel(logrus.DebugLevel)

	// 使用rotatelogs完成日志分割、日志定期清理、生成软链文件指向最新日志
	InfologWriter, _ := rotatelogs.New(
		// 分割后的文件名称
		infologFile+".%Y%m%d",
		// 生成软链，指向最新日志文件
		rotatelogs.WithLinkName(infologFile),
		// 设置最大保存时间(7天)
		rotatelogs.WithMaxAge(7*24*time.Hour),
		// 设置日志切割时间间隔(1天)
		rotatelogs.WithRotationTime(24*time.Hour),
	)

	DebuglogWriter, _ := rotatelogs.New(
		// 分割后的文件名称
		infologFile+"-debug.%Y%m%d",
		// 生成软链，指向最新日志文件
		// rotatelogs.WithLinkName(debuglogFile),
		// 设置最大保存时间(7天)
		rotatelogs.WithMaxAge(7*24*time.Hour),
		// 设置日志切割时间间隔(1天)
		rotatelogs.WithRotationTime(24*time.Hour),
	)

	ErrorlogWriter, _ := rotatelogs.New(
		// 分割后的文件名称
		infologFile+"-error.%Y%m%d",
		// 生成软链，指向最新日志文件
		// rotatelogs.WithLinkName(errorlogFile),
		// 设置最大保存时间(7天)
		rotatelogs.WithMaxAge(7*24*time.Hour),
		// 设置日志切割时间间隔(1天)
		rotatelogs.WithRotationTime(24*time.Hour),
	)

	// 使用lfshook决定哪些级别的日志可以使用rotatelogs的切割设置，并决定输出格式（TEXT / JSON）。
	lfHook := lfshook.NewHook(lfshook.WriterMap{
		logrus.DebugLevel: DebuglogWriter,
		logrus.InfoLevel:  InfologWriter,
		logrus.WarnLevel:  ErrorlogWriter,
		logrus.ErrorLevel: ErrorlogWriter,
		logrus.FatalLevel: ErrorlogWriter,
		logrus.PanicLevel: ErrorlogWriter,
	}, &logrus.JSONFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})

	// 新增 Hook
	Logger.AddHook(lfHook)

	return func(c *gin.Context) {
		// 一.从gin中获取请求响应相关信息
		// 开始时间
		startTime := time.Now()
		// 调用该请求的剩余处理程序
		c.Next()
		// 结束时间
		endTime := time.Since(startTime)
		// 执行时间
		latencyTime := fmt.Sprintf("%d ms", int(float64(endTime.Nanoseconds()/1000000)))

		// 请求IP
		clientIP := c.ClientIP()
		remoteIP := c.RemoteIP()

		// 请求方式
		reqMethod := c.Request.Method
		// 请求路由
		reqUri := c.Request.RequestURI
		// 请求协议
		reqProto := c.Request.Proto
		// 请求状态码
		resCode := c.Writer.Status()
		// 请求响应内容长度
		resLength := c.Writer.Size()
		if resLength < 0 {
			resLength = 0
		}
		// 请求来源
		repReferer := c.Request.Referer()
		// 请求UA
		reqUA := c.Request.UserAgent()

		// 二.从标准记录器创建一个条目，并向其中添加多个字段(隐式添加 log 本身的时间戳,信息等 fields )
		// 日志格式 (以Nginx日志为例)
		// xx.xx.xx.xx - - [12/Apr/2023:21:34:56 +0800] "GET /sites/ HTTP/2.0" 200 13417 "https://blog.weiyigeek.top/links/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0" "-" rt=0.000 urt=-
		LogEntry := Logger.WithFields(logrus.Fields{
			"client_ip":    clientIP,
			"remote_ip":    remoteIP,
			"req_method":   reqMethod,
			"req_uri":      reqUri,
			"req_proto":    reqProto,
			"res_code":     resCode,
			"res_length":   resLength,
			"req_referer":  repReferer,
			"req_ua":       reqUA,
			"latency_time": latencyTime,
		})

		// Errors 保存了使用当前context的所有中间件/handler 所产生的全部错误信息。
		// 源码注释： Errors is a list of errors attached to all the handlers/middlewares who used this context.
		// 三.将系统内部的错误 log 出去
		if len(c.Errors) > 0 {
			LogEntry.Error(c.Errors.ByType(gin.ErrorTypePrivate).String())
		}

		// 四.根据状态码决定打印 log 的等级
		if resCode >= 500 {
			LogEntry.Error()
		} else if resCode >= 400 {
			LogEntry.Warn()
		} else {
			LogEntry.Info()
		}

	}
}

// 日志记录到 MongoDB
func LoggerToMongo() gin.HandlerFunc {
	return func(c *gin.Context) {
	}
}

// 日志记录到 ES
func LoggerToES() gin.HandlerFunc {
	return func(c *gin.Context) {
	}
}

// 日志记录到 MQ
func LoggerToMQ() gin.HandlerFunc {
	return func(c *gin.Context) {
	}
}
